<!--
 * @description: 
 * @Author: Peng_Yong
 * @Date: 2023-08-18 13:07:43
 * @LastEditors: James324
 * @LastEditTime: 2024-01-25 09:31:10
-->

<template>
    <div id="computedSearchForm" ref="searchRef" class="searchForm">
        <el-form
            v-bind="searchConfig.formProps"
            ref="ElFormRef"
            class="search-box"
            :model="formData"
            :label-width="labelWidth || '120px'"
            label-position="right"
        >
            <div class="q-row" :class="{ isDialogSearch, 'close-minWidth': closeMinWidth }">
                <div
                    v-for="{
                        slotName,
                        elInputProps,
                        elInputNumberProps,
                        elDatePickerProps,
                        elSelectProps,
                        elTimePickerProps,
                        itemType,
                        modelKey,
                        validFormatType,
                        columnProps
                    } in searchConfig.items"
                    :key="modelKey"
                    class="q-col"
                >
                    <template v-if="slotName">
                        <el-form-item
                            v-bind="columnProps"
                            :label="handleLabel(columnProps?.label as string)"
                        >
                            <slot :name="slotName" :row="formData"></slot>
                        </el-form-item>
                    </template>

                    <template v-else>
                        <el-form-item
                            v-bind="columnProps"
                            :label="handleLabel(columnProps?.label as string)"
                        >
                            <el-input
                                v-if="itemType === 'text'"
                                v-bind="elInputProps"
                                v-model.trim="formData[modelKey!]"
                                class="baseInput"
                                :prop="modelKey"
                                @input="
                                    formData[modelKey!] = formatInput(
                                        formData,
                                        modelKey!,
                                        validFormatType
                                    )
                                "
                            />
                            <el-input-number
                                v-else-if="itemType === 'number'"
                                v-bind="elInputNumberProps"
                                v-model="formData[modelKey!]"
                                clearable
                                :prop="modelKey"
                            />
                            <el-date-picker
                                v-else-if="itemType === 'date'"
                                v-model="formData[modelKey!]"
                                value-format="YYYY-MM-DD"
                                clearable
                                v-bind="elDatePickerProps"
                                :prop="modelKey"
                            />
                            <el-time-picker
                                v-else-if="itemType === 'time'"
                                v-model="formData[modelKey!]"
                                value-format="YYYY-MM-DD hh:mm:ss"
                                clearable
                                v-bind="elTimePickerProps"
                                :prop="modelKey"
                            />
                            <el-select
                                v-else-if="itemType === 'select'"
                                v-model="formData[modelKey!]"
                                fit-input-width
                                clearable
                                v-bind="elSelectProps"
                            >
                                <el-option
                                    v-for="(item, index) in elSelectProps?.options"
                                    :key="
                                        index +
                                        ((elSelectProps?.optionLabelKey &&
                                            item[elSelectProps.optionLabelKey]) ??
                                            item.label ??
                                            item)
                                    "
                                    :label="
                                        elSelectProps?.optionLabelKey
                                            ? item[elSelectProps.optionLabelKey]
                                            : item.label ?? item
                                    "
                                    :value="
                                        elSelectProps?.optionValueKey
                                            ? item[elSelectProps.optionValueKey]
                                            : item.value ?? item
                                    "
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </template>
                </div>

                <search-controller
                    ref="controllerRef"
                    :isCloseOpen="isCloseOpen"
                    :searchRef="searchRef"
                >
                    <el-button type="primary" @click="handleSearch">查询</el-button>
                    <el-button @click="handleRefresh">重置</el-button>
                    <slot />
                </search-controller>
                <slot name="other" />
            </div>
        </el-form>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { handleFormData, handleLabel, formatInput } from '@/utils';
import type { FormInstance } from 'element-plus';
import searchController from './components/search-controller.vue';
import { cloneDeep } from 'lodash-es';

const props = withDefaults(
    defineProps<{
        searchConfig: PageConfig;
        isCloseOpen?: boolean;
        closeMinWidth?: boolean; // 关闭输入选项最小宽的限制
        tableRef?: TableRef;
        resetData?: (reset?: boolean) => Promise<void>;
        handleParams?: (form: anyObj) => anyObj;
    }>(),
    {
        isCloseOpen: false
    }
);

// 表单父级Ref
const searchRef = ref<HTMLDivElement>();

// 表单Ref
const ElFormRef = ref<FormInstance>();

// 是否是弹框内的搜索框
const isDialogSearch = computed(
    () => searchRef.value?.parentElement?.className === 'm-modal__body'
);

const { params, date } = handleFormData(props.searchConfig);
// 时间数据
const dateObj = date;
// 表单数据
const formData = ref<anyObj>(params);

/**
 * @: label宽度
 */
const labelWidth = computed(() => {
    let maxLength = 0;
    const objects = props.searchConfig.items;
    for (let i = 0; i < objects.length; i++) {
        const item = objects[i].columnProps?.label?.length || 0;
        if (item > maxLength) {
            maxLength = item;
        }
    }
    if (maxLength < 3) return '44px';

    return (maxLength - 2) * 14 + 44 + 'px';
});

const emit = defineEmits(['search', 'refresh']);

/**
 * @: 点击搜索
 */
const handleSearch = useThrottleFn(() => {
    let form = getForm();
    if (props.handleParams) {
        form = props.handleParams(form);
    }
    // 先查看有没有规则
    if (props.searchConfig.formProps?.rules) {
        ElFormRef.value?.validate(valid => {
            if (valid) {
                props.tableRef?.getData?.(form);
                emit('search', form);
            }
        });
    } else {
        props.tableRef?.getData?.(form);
        emit('search', form);
    }
});

/**
 * @: 获取数据form数据
 */
const getForm = () => {
    const fnString = props.tableRef?.getDataFn?.toString() || '';
    const isPost = fnString.toLocaleUpperCase().includes('GET');
    const key = isPost ? 'get' : 'post';
    return formKeyObj[key]();
};

/**
 * @: post请求时需要处理的数据
 */
const post = () => {
    const obj = cloneDeep(formData.value);
    for (const key in dateObj) {
        obj[key] = {
            start: obj[key]?.[0] || '',
            end: obj[key]?.[1] || ''
        };
    }
    return obj;
};

/**
 * @: get请求时需要处理的数据
 */
const get = () => {
    const obj = cloneDeep(formData.value);
    for (const key in dateObj) {
        obj[`${key}.start`] = obj[key]?.[0] || '';
        obj[`${key}.end`] = obj[key]?.[1] || '';
        Reflect.deleteProperty(obj, key);
    }
    return obj;
};

const formKeyObj = {
    get,
    post
};

const handleRefresh = () => {
    emit('refresh');

    nextTick(() => {
        formData.value = handleFormData(props.searchConfig).params;
        if (props.tableRef) {
            props.tableRef.getData?.({ current: 1, size: 20 });
        }
        if (props.resetData) {
            props.resetData(true);
        }
    });
};
const controllerRef = ref<InstanceType<typeof searchController>>();
const refreshCol = () => {
    controllerRef.value?.computeCol();
};

defineExpose({
    formData,
    getForm,
    handleRefresh,
    refreshCol
});
</script>

<style lang="scss" scoped>
.searchForm {
    --q-input-width: 225px;
    --q-input-height: 32px;

    .el-form {
        --el-form-inline-content-width: var(--q-input-width);
    }

    :deep(.el-input) {
        --el-input-height: var(--q-input-height);
        --el-date-editor-width: 100%;

        min-width: var(--q-input-width);
    }

    :deep(.el-range-editor.el-input__wrapper) {
        min-width: calc(var(--q-input-width) - 22px);
        height: calc(var(--q-input-height) - 2px);
        padding: 1px 11px;

        .el-range-input {
            height: 30px;
        }
    }

    :deep(.el-form-item) {
        margin-bottom: 16px;

        .el-form-item__label {
            height: var(--q-input-height);
            line-height: var(--q-input-height);
        }
    }
}

.search-box {
    overflow: hidden;
    transition: all 0.2s ease-in;
}

@include b(isDialogSearch) {
    --q-input-width: 203px;

    @include b(q-col) {
        flex: 0 0 33% !important;
        max-width: 33% !important;
    }
}

.el-select {
    width: 100%;
    min-width: var(--q-input-width);

    :deep(.el-input) {
        width: 100%;
    }
}

@include b(isDialogSearch) {
    --q-input-width: 203px;

    &.close-minWidth {
        --q-input-width: auto !important;
    }

    @include b(q-col) {
        flex: 0 0 33% !important;
        max-width: 33% !important;
    }
}

@include b(q-row) {
    position: relative;
    box-sizing: border-box;
    display: flex;
    flex-wrap: wrap;
    margin: 0 -7px;
}

@include b(q-col) {
    box-sizing: border-box;
    flex: 0 0 25%;
    max-width: 25%;
    padding: 0 7px;
}

@media (width >= 2000px) {
    @include b(q-col) {
        flex: 0 0 20%;
        max-width: 20%;
    }
}

@media (width <= 1650px) {
    @include b(q-col) {
        flex: 0 0 33%;
        max-width: 33%;
    }
}
</style>
